package com.bstek.bdf4.utils;

import cn.hutool.core.codec.Base64;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.bstek.bdf4.exception.UtilException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
import org.springframework.util.Assert;

import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.method.HandlerMethod;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.util.Optional;

/**
 * web工具类
 */

@Slf4j
public class WebUtils extends org.springframework.web.util.WebUtils {
  private final String BASIC_ = "Bstek ";

  private final String UNKNOWN = "unknown";

  /**
   * 判断是否ajax请求 spring ajax 返回含有 ResponseBody 或者 RestController注解
   * @param handlerMethod HandlerMethod
   * @return 是否ajax请求
   */
  public boolean isBody(HandlerMethod handlerMethod) {

    ResponseBody responseBody = ClassUtils.getAnnotation(handlerMethod, ResponseBody.class);
    return responseBody != null;
  }

  /**
   * 读取cookie
   * @param name cookie name
   * @return cookie value
   */
  public String getCookieVal(String name) {
    HttpServletRequest request = WebUtils.getRequest();
    Assert.notNull(request, "request from RequestContextHolder is null");
    return getCookieVal(request, name);
  }

  /**
   * 读取cookie
   * @param request HttpServletRequest
   * @param name cookie name
   * @return cookie value
   */
  public String getCookieVal(HttpServletRequest request, String name) {
    Cookie cookie = getCookie(request, name);
    return cookie != null ? cookie.getValue() : null;
  }

  /**
   * 清除 某个指定的cookie
   * @param response HttpServletResponse
   * @param key cookie key
   */
  public void removeCookie(HttpServletResponse response, String key) {
    setCookie(response, key, null, 0);
  }

  /**
   * 设置cookie
   * @param response HttpServletResponse
   * @param name cookie name
   * @param value cookie value
   * @param maxAgeInSeconds maxage
   */
  public void setCookie(HttpServletResponse response, String name, String value, int maxAgeInSeconds) {
    Cookie cookie = new Cookie(name, value);
    cookie.setPath("/");
    cookie.setMaxAge(maxAgeInSeconds);
    cookie.setHttpOnly(true);
    response.addCookie(cookie);
  }

  /**
   * 获取 HttpServletRequest
   * @return {HttpServletRequest}
   */
  public static HttpServletRequest getRequest() {
    try {
      RequestAttributes requestAttributes = RequestContextHolder.currentRequestAttributes();
      return ((ServletRequestAttributes) requestAttributes).getRequest();
    }
    catch (IllegalStateException e) {
      return null;
    }
  }

  /**
   * 获取 HttpServletResponse
   * @return {HttpServletResponse}
   */
  public HttpServletResponse getResponse() {
    return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
  }

  /**
   * 返回json
   * @param response HttpServletResponse
   * @param result 结果对象
   */
  public void renderJson(HttpServletResponse response, Object result) {
    renderJson(response, result, MediaType.APPLICATION_JSON_VALUE);
  }

  /**
   * 返回json
   * @param response HttpServletResponse
   * @param result 结果对象
   * @param contentType contentType
   */
  public void renderJson(HttpServletResponse response, Object result, String contentType) {
    response.setCharacterEncoding("UTF-8");
    response.setContentType(contentType);
    try (PrintWriter out = response.getWriter()) {
      out.append(JSONUtil.toJsonStr(result));
    }
    catch (IOException e) {
      log.error(e.getMessage(), e);
    }
  }

  /**
   * 获取ip
   * @return {String}
   */
  public  String getIP() {
    return getIP(WebUtils.getRequest());
  }

  /**
   * 获取ip
   * @param request HttpServletRequest
   * @return {String}
   */
  public  String getIP(HttpServletRequest request) {
    Assert.notNull(request, "HttpServletRequest is null");
    String ip = request.getHeader("X-Requested-For");
    if (StrUtil.isBlank(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
      ip = request.getHeader("X-Forwarded-For");
    }
    if (StrUtil.isBlank(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
      ip = request.getHeader("Proxy-Client-IP");
    }
    if (StrUtil.isBlank(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
      ip = request.getHeader("WL-Proxy-Client-IP");
    }
    if (StrUtil.isBlank(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
      ip = request.getHeader("HTTP_CLIENT_IP");
    }
    if (StrUtil.isBlank(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
      ip = request.getHeader("HTTP_X_FORWARDED_FOR");
    }
    if (StrUtil.isBlank(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
      ip = request.getRemoteAddr();
    }
    return StrUtil.isBlank(ip) ? null : ip.split(",")[0];
  }

  /**
   * 解析 client id
   * @param header
   * @param defVal
   * @return 如果解析失败返回默认值
   */
  public String extractClientId(String header, final String defVal) {

    if (header == null || !header.startsWith(BASIC_)) {
      log.debug("请求头中client信息为空: {}", header);
      return defVal;
    }
    byte[] base64Token = header.substring(6).getBytes(StandardCharsets.UTF_8);
    byte[] decoded;
    try {
      decoded = Base64.decode(base64Token);
    }
    catch (IllegalArgumentException e) {
      log.debug("Failed to decode basic authentication token: {}", header);
      return defVal;
    }

    String token = new String(decoded, StandardCharsets.UTF_8);

    int delim = token.indexOf(":");

    if (delim == -1) {
      log.debug("Invalid basic authentication token: {}", header);
      return defVal;
    }
    return token.substring(0, delim);
  }

  /**
   * 从请求头中解析 client id
   * @param header
   * @return
   */
  public Optional<String> extractClientId(String header) {
    return Optional.ofNullable(extractClientId(header, null));
  }

  /**
   * 从request 获取CLIENT_ID
   * @return
   */
  public String getClientId(String header) {
    String clientId = extractClientId(header, null);
    if (clientId == null) {
      throw new UtilException("Invalid basic authentication token");
    }
    return clientId;
  }


}
